//
//    Copyright (C) 2012  OinkStudio
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.
package game;

import java.io.Serializable;

public class Game implements Serializable {

	public static final int RIVAL = 0;
	public static final int USER = 1;

	// Sugerencias, cada vegada que es faci un check, guardar la iteracio on es
	// fa el check i el nom de la regla que es fa servir en aquella iteracio
	// aixi es podra fer un seguiment de com es giren las cartes. En comptes de
	// canviar el owner dels obolos que es giren en els chekcs, guardar que s'ha
	// de girar el obolo al final de la comprobacio de totes les reglas i
	// guardar en quina norma es giraran(iteracio). Possible crear una classe
	// Progess on guardar tota aquesta informacio

	private Obolo[][] board;
	private Obolo[][] selectDecks;
	private int lastRow;
	private int lastColumn;
	private Player[] players;
	private int turn;
	private int blank;
	private Rule[] rules;

	public Game(int row, int column, int blank) {
		board = new Obolo[row][column];
		this.blank = blank;
		turn = 0;
	}

	public int getRowNumber() {
		return board.length;
	}

	public int getBlank() {
		return blank;
	}

	public int getColumnNumber() {
		return board[0].length;
	}

	public int getTurn() {
		return turn;
	}

	public void changeTurn() {
		if (turn == 0)
			turn = 1;
		else
			turn = 0;
	}

	public int getLastRow() {
		return lastRow;
	}

	public int getLastColumn() {
		return lastColumn;
	}

	public Rule[] getRules() {
		return rules;
	}

	public void setBoard(Obolo[][] board) {
		this.board = board;
	}

	public void setRules(Rule[] rules) {
		this.rules = rules;
	}

	public void setRules(String[] strRules) {
		Rule[] rules = null;

		if (strRules != null) {
			int rulesLength = strRules.length;

			boolean isDownWard = false;
			boolean isChain = false;

			if (!strRules[0].equals("")) {
				// Revisa els modificadors de les normes a la baixa i cadena
				for (String nameRule : strRules) {
					if (nameRule.equals("downward"))
						isDownWard = true;
					if (nameRule.equals("chain"))
						isChain = true;
				}

				if (!isDownWard)
					rulesLength++;
				if (isChain)
					rulesLength--;

				// Crea l'array de normes segons les normes que s'han de afegir
				rules = new Rule[rulesLength];
				int sincron = 0;

				for (int i = 0; i < rules.length; i++) {
					if (i == 0) {
						if (strRules[i].equals("downward"))
							rules[i] = RuleDownward.getInstance();
						else {
							rules[i] = RuleUpward.getInstance();
							sincron++;
						}
					} else {
						if (strRules[i - sincron].equals("sum")) {
							if (isChain)
								rules[i] = RuleSumChain.getInstance();
							else
								rules[i] = RuleSum.getInstance();
						}
						if (strRules[i - sincron].equals("equal")) {
							if (isChain)
								rules[i] = RuleEqualChain.getInstance();
							else
								rules[i] = RuleEqual.getInstance();
						}
					}
				}
			}
		}
		// Si no hi havia cap norma seleccionada es crea nomes la Upward
		// (Alta)
		setRules(rules);
	}

	public void setRules(Rule rule) {
		this.rules = new Rule[1];
		this.rules[0] = rule;
	}

	public Player getPlayer(int numberPlayer) {
		return players[numberPlayer];
	}

	public Rule getOneRule(int numberRule) {
		return rules[numberRule];
	}

	public void setPlayers(Player rival, Player user)
			throws NullPointerException {
		players = new Player[2];
		try {
			rival.setOwnerId(0);
			players[0] = rival;
			user.setOwnerId(1);
			players[1] = user;
			// Copia per que el guanyador seleccioni obolos del perdedor
			setSelectDecks();
		} catch (NullPointerException e) {
			throw e;
		}
	}

	public Obolo[][] getBoard() {
		return board;
	}

	public void putObolo(int row, int column, Obolo obolo)
			throws IllegalArgumentException {
		if (board[row][column] == null) {
			board[row][column] = obolo;
			// S'actualitza l'estat del obolo usat pel jugador actual
			getPlayer(turn).setOboloUsed(obolo);
			obolo = null;
			checkRules(row, column);
			changeTurn();
		} else
			throw new IllegalArgumentException();
	}

	private void setSelectDecks() {
		selectDecks = new Obolo[players.length][players[0].getDeck().length];
		for (int indexPlayer = 0; indexPlayer < players.length; indexPlayer++) {
			for (int indexDeck = 0; indexDeck < players[indexPlayer].getDeck().length; indexDeck++) {
				try {
					if (players[indexPlayer].getDeck()[indexDeck] != null)
						selectDecks[indexPlayer][indexDeck] = players[indexPlayer]
								.getDeck()[indexDeck].clone();
				} catch (CloneNotSupportedException e) {

				}
			}
		}
	}

	public Player whoWin() {
		// Retorna el jugador guanyador amb el deck del adversari per robar una
		// carta
		int player0 = 0;
		int player1 = 0;
		for (int i = 0; i < board.length; i++) {
			for (int j = 0; j < board[0].length; j++) {
				if (board[i][j].getOwner() == 0)
					player0++;
				else
					player1++;
			}
		}

		for (int i = 0; i < getPlayerUsedDeck(0).length; i++) {
			if (getPlayerUsedDeck(0)[i] == 0)
				player0++;
			if (getPlayerUsedDeck(1)[i] == 0)
				player1++;
		}

		if (player0 == player1)
			return null;
		else if (player0 > player1) {
			players[0].setDeck(selectDecks[1]);
			return players[0];
		} else {
			players[1].setDeck(selectDecks[0]);
			return players[1];
		}
	}

	public void checkRules(int row, int column) {
		for (int i = rules.length - 1; i >= 0; i--) {
			if(rules[i] != null){
				rules[i].check(row, column, this);
			}
			
		}
	}

	public boolean isFinish() {
		boolean finished = true;
		for (int i = 0; i < board.length; i++) {
			for (int j = 0; j < board[0].length; j++) {
				if (board[i][j] == null)
					finished = false;
			}
		}
		return finished;
	}

	public String getPlayerName(int playerIndex) throws NullPointerException {
		try {
			return players[playerIndex].getName();
		} catch (NullPointerException e) {
			throw e;
		}
	}

	public Obolo[] getPlayerDeck(int playerIndex) throws NullPointerException {
		try {
			return players[playerIndex].getDeck();
		} catch (NullPointerException e) {
			throw e;
		}
	}
	
	public int[] getPlayerUsedDeck(int playerIndex) throws NullPointerException {
		try {
			return players[playerIndex].getUsedDeck();
		} catch (NullPointerException e) {
			throw e;
		}
	}

}
